package top.wshape1.shiziapi.project.handler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.wshape1.shiziapi.common.constant.RedisConstant;
import top.wshape1.shiziapi.common.domain.ProjectSearch;
import top.wshape1.shiziapi.common.domain.to.DateGapTo;
import top.wshape1.shiziapi.common.utils.R;
import top.wshape1.shiziapi.project.domain.Like;
import top.wshape1.shiziapi.project.domain.Read;
import top.wshape1.shiziapi.project.domain.vo.TwoIdVo;
import top.wshape1.shiziapi.project.feign.SearchFeignService;
import top.wshape1.shiziapi.project.service.*;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2024-01-23
 */

@Component
@Slf4j
public class XxlJobHandler {

    @Resource
    private ProjectService projectService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private LikeService likeService;

    @Resource
    private ReadService readService;

    @Resource
    private SearchFeignService searchFeignService;

    @Resource
    private DocumentService documentService;

    @Resource
    private ApiService apiService;

    @Resource
    private ApiTagService apiTagService;

    @Resource
    private ApiContentService apiContentService;

    @Resource
    private MarkdownService markdownService;

    @Resource
    private MarkdownContentService markdownContentService;

    @Resource
    private RtfService rtfService;

    @Resource
    private RtfContentService rtfContentService;

    /**
     * 定时保存项目点赞信息
     */
    @XxlJob("SaveProjectLikeHandler")
    @Transactional
    public void processLike() {

        log.info("processLike waiting for lock 。。。");
        RLock lock = redissonClient.getLock(RedisConstant.PROJECT_LIKE_LOCK_KEY);
        if (lock.tryLock()) {
            log.info("processLike start 。。。");

            try {
                List<Long> ids = projectService.getPublicProjectIds();


                for (Long projectId : ids) {
                    String noteLikeSetName = RedisConstant.PROJECT_NOTE_LIKE_KEY + projectId;
                    String noteUnlikeSetName = RedisConstant.PROJECT_NOTE_UNLIKE_KEY + projectId;

                    Objects.requireNonNull(redisTemplate.opsForSet().members(noteLikeSetName)).forEach(
                            userId -> {
                                Like like = new Like();
                                like.setUserId(Long.valueOf(String.valueOf(userId)));
                                like.setProjectId(projectId);
                                likeService.save(like);
                            }
                    );

                    Objects.requireNonNull(redisTemplate.opsForSet().members(noteUnlikeSetName)).forEach(
                            userId -> likeService.remove(new LambdaUpdateWrapper<Like>()
                                    .eq(Like::getUserId, userId)
                                    .eq(Like::getProjectId, projectId)));

                    redisTemplate.delete(noteLikeSetName);
                    redisTemplate.delete(noteUnlikeSetName);

                    // 检查
                    if (likeService.countLike(projectId) != likeService.count(new LambdaQueryWrapper<Like>().eq(Like::getProjectId, projectId))) {
                        List<Like> list = likeService.list(new LambdaUpdateWrapper<Like>().eq(Like::getProjectId, projectId));
                        redisTemplate.delete(RedisConstant.PROJECT_LIKE_SET_KEY + projectId);
                        if (list != null && !list.isEmpty()) {
                            List<Long> userIdList = list.stream().map(Like::getUserId).toList();
                            redisTemplate.opsForSet().add(RedisConstant.PROJECT_LIKE_SET_KEY + projectId, userIdList);
                        }
                    }

                }
            } finally {
                lock.unlock();
                log.info("processLike end 。。。");
            }
        }
    }

    /**
     * 定时保存项目浏览量
     */
    @XxlJob("SaveProjectReadHandler")
    @Transactional
    public void processRead() {

        log.info("processRead Start 。。。");

        // 过去三小时有更新的数据
        Date endTime = new Date();
        Date startTime = new Date(endTime.getTime() - 3 * 60 * 60 * 1000);

        R<List<ProjectSearch>> listR = searchFeignService.readList(new DateGapTo(startTime, endTime));
        if (listR.isSuccessful()) {

            listR.getData().forEach(projectSearch -> {
                readService.save(new Read(projectSearch.getId(), projectSearch.getReadCount()));
            });

            log.info("processRead Success 。。。");
        } else {
            log.info("processRead Failed 。。。");
        }
    }

    /**
     * 缓存热点项目内容
     */
    @XxlJob("cacheHotProjectHandler")
    public void cacheHotProject() {
        log.info("cacheHotProject Start 。。。");

        R<List<ProjectSearch>> hot = searchFeignService.hot();

        if (hot.isSuccessful()) {

            /* Redis Hash
               project_hot_key ->   | name -> 项目名字
                                    | docList -> 项目内文档列表
                                    | {文档id} -> 文档内容
             */
            hot.getData().forEach(projectSearch -> {
                Long projectId = projectSearch.getId();
                String redisKey = RedisConstant.PROJECT_HOT_KEY + projectId;
                HashMap<String, Object> map = new HashMap<>();

                try {
                    // name
                    map.put(RedisConstant.PROJECT_HOT_NAME_KEY, projectSearch.getName());
                    // docList
                    map.put(RedisConstant.PROJECT_HOT_DOCLIST_KEY, documentService.docList(projectId, null));

                    // 缓存文档

                    // 缓存Markdown文档
                    List<Long> mdIds = markdownService.getIdsByProjectId(projectId);
                    if (mdIds != null && !mdIds.isEmpty()) {
                        mdIds.forEach(id -> map.put(RedisConstant.PROJECT_HOT_MD_KEY + id, markdownContentService.getById(id).getContent()));
                    }

                    // 缓存Rtf文档
                    List<Long> rtfIds = rtfService.getIdsByProjectId(projectId);
                    if (rtfIds != null && !rtfIds.isEmpty()) {
                        rtfIds.forEach(id -> map.put(RedisConstant.PROJECT_HOT_RTF_KEY + id, rtfContentService.getById(id).getContent()));
                    }

                    // 缓存Api文档
                    List<Long> apiIds = apiService.getIdsByProjectId(projectId);
                    if (apiIds != null && !apiIds.isEmpty()) {
                        apiIds.forEach(id -> {
                            map.put(RedisConstant.PROJECT_HOT_API_KEY + id, apiService.getApiData(new TwoIdVo(id, projectId), null));
                            map.put(RedisConstant.PROJECT_HOT_APITAG_KEY + id, apiService.listTag(new TwoIdVo(id, projectId), null));
                        });
                    }
                } catch (Exception ignored) {
                    // 如果报错，可能是该项目突然变私有了。
                    // 停止缓存
                    return;
                }
                redisTemplate.opsForHash().putAll(redisKey, map);
                redisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
            });

            log.info("cacheHotProject Success 。。。");
        } else {
            log.info("cacheHotProject Failed 。。。");
        }

    }

}
